package com.ywf.corebase.framework.interceptor;

import com.ywf.corebase.common.constant.HeaderConstants;
import com.ywf.corebase.common.exception.BusinessException;
import com.ywf.corebase.common.utils.ApiUtil;
import com.ywf.corebase.common.utils.MD5Util;
import com.ywf.corebase.common.utils.StringUtil;
import com.ywf.corebase.framework.annotation.NotCheckParam;
import com.ywf.corebase.framework.annotation.NotRepeatSubmit;
import com.ywf.corebase.framework.enums.CallSourceEnum;
import com.ywf.corebase.framework.enums.ResultCodeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * ClassName:HeaderParamsCheckInterceptor
 * Package:com.ywf.myproject.common.interceptor
 * Description:
 *
 * @Date:2021/3/10 6:43
 * @Author:YWF
 */
public class HeaderParamsCheckInterceptor implements HandlerInterceptor {

    final static Logger logger = LoggerFactory.getLogger(TokenInterceptor.class);

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info("==========================请求头拦截器===================================");
        if (handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            final Class<?> clazz = handlerMethod.getBeanType();
            final Method method = handlerMethod.getMethod();
            if ((!clazz.isAnnotationPresent(NotCheckParam.class)) && (!method.isAnnotationPresent(NotCheckParam.class))){
                /*获取APP请求头参数*/
                String callSource = request.getHeader(HeaderConstants.CALL_SOURCE);
                String appVersion = request.getHeader(HeaderConstants.APP_VERSION);
                String appId = request.getHeader(HeaderConstants.X_APPID);
                String timestamp = request.getHeader(HeaderConstants.X_TIMESTAMP);
                String nonce = request.getHeader(HeaderConstants.X_NONCE);
                String sign = request.getHeader(HeaderConstants.X_SIGN);
                /*校验请求头参数*/
                if (StringUtil.isAnyBlank(callSource,timestamp,nonce,sign)){
                    throw new BusinessException(ResultCodeEnum.PARAM_NOT_COMPLETE);
                }
                /*校验Android和ios必须要携带App版本号*/
                if ((CallSourceEnum.ANDROID.name().equals(callSource) || CallSourceEnum.IOS.name().equals(callSource)) && StringUtil.isEmpty(appVersion)){
                    throw new BusinessException(ResultCodeEnum.PARAM_NOT_COMPLETE);
                }
                /*调用来源校验*/
                if (!CallSourceEnum.isValid(callSource)){
                    throw new BusinessException(ResultCodeEnum.PARAM_IS_INVALID);
                }
                /*检验时间戳类型*/
                try {
                    Long.valueOf(timestamp);
                } catch (NumberFormatException e) {
                    throw new BusinessException(ResultCodeEnum.PARAM_IS_INVALID);
                }
                // 获取超时时间
                NotRepeatSubmit notRepeatSubmit = ApiUtil.getNotRepeatSubmit(handler);
                long expireTime = notRepeatSubmit == null ? 5 * 60 * 1000 : notRepeatSubmit.value();

                // 2. 请求时间间隔
                long reqeustInterval = System.currentTimeMillis() - Long.valueOf(timestamp);
                if (reqeustInterval > expireTime){
                    throw new BusinessException(ResultCodeEnum.REQUEST_TIME_OUT);
                }

                // 4. 校验签名(将所有的参数加进来，防止别人篡改参数) 所有参数看参数名升续排序拼接成url
                // 请求参数 + token + timestamp + nonce
                String appSecret = "123456789";
                String signString = ApiUtil.concatSignString(request) + appSecret + timestamp + nonce;
                String signature = MD5Util.encode(signString);
                logger.info("signature:"+signature);
                boolean flag = signature.equals(sign);
                if (!flag){
                    throw new BusinessException(ResultCodeEnum.SIGN_ERROR);
                }

                // 5. 拒绝重复调用(第一次访问时存储，过期时间和请求超时时间保持一致), 只有标注不允许重复提交注解的才会校验
                if (notRepeatSubmit != null) {
                    ValueOperations<String, Integer> signRedis = redisTemplate.opsForValue();
                    boolean exists = redisTemplate.hasKey(sign);
                    if (exists){
                        throw new BusinessException(ResultCodeEnum.SIGN_ERROR);
                    }
                    signRedis.set(sign, 0, expireTime, TimeUnit.MILLISECONDS);
                }
                ValueOperations<String, Integer> signRedisTs = redisTemplate.opsForValue();
                logger.info("是否已请求："+signRedisTs.get(sign));
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }

}
